package gapidt.plugin.wizards;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.ISharedImages;

public class TypesSelectorTree{

	private Map<ImageDescriptor, Image> imageCache = new HashMap<ImageDescriptor, Image>();
	
	Tree tree;
	
	public TypesSelectorTree(Composite parent, IJavaProject javaProject, int width, int height) {
		
		tree = new Tree(parent, SWT.BORDER | SWT.MULTI | SWT.CHECK | SWT.V_SCROLL);
		tree.setBounds(0, 0, width, height);
		tree.setSize(width, height);
		
		GridData gridData = new GridData();
	    gridData.widthHint = width;
	    gridData.heightHint = height;
	    tree.setLayoutData(gridData);
	    
		TreeItem rootItem  = new TreeItem(tree, SWT.NONE);
		rootItem.setText(javaProject.getElementName());
		rootItem.setData(javaProject);
		rootItem.setImage(getImage(javaProject));
		
		try {
			for(IPackageFragmentRoot pkg : javaProject.getPackageFragmentRoots())
				if(pkg.getKind() == IPackageFragmentRoot.K_SOURCE)
					addJavaElementItem(rootItem, pkg);
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		
		tree.addListener(SWT.Selection, new Listener() {
	        public void handleEvent(Event event) {
	            if (event.detail == SWT.CHECK) {
	                TreeItem item = (TreeItem) event.item;
	                boolean checked = item.getChecked();
	                checkItems(item, checked);
	                checkPath(item.getParentItem(), checked, false);
	            }
	        }
	    });

		
	}
	
	protected void addSelectionListener(Listener listener){
		tree.addListener(SWT.Selection, listener);
	}
	
	protected void addJavaElementItem(TreeItem parent, IJavaElement javaElement) throws JavaModelException{
		
		if(javaElement instanceof IPackageFragmentRoot && !((IPackageFragmentRoot) javaElement).hasChildren() || javaElement instanceof IPackageFragment && !((IPackageFragment) javaElement).hasChildren())
			return;
			
		TreeItem item = new TreeItem(parent, SWT.NONE);
		item.setText(javaElement.getElementName());
		item.setData(javaElement);
		item.setImage(getImage(javaElement));
		
		if(javaElement instanceof IPackageFragmentRoot)
			for(IJavaElement child : ((IPackageFragmentRoot) javaElement).getChildren())
				addJavaElementItem(item, child);
		else if(javaElement instanceof IPackageFragment)
			for(IJavaElement child : ((IPackageFragment) javaElement).getChildren())
				addJavaElementItem(item, child);
		else if(javaElement instanceof ICompilationUnit)
			for(IType child : ((ICompilationUnit) javaElement).getTypes())
				addJavaElementItem(item, child);
			
	}
	
	static protected void checkPath(TreeItem item, boolean checked, boolean grayed) {
	    if (item == null) return;
	    if (grayed) {
	        checked = true;
	    } else {
	        int index = 0;
	        TreeItem[] items = item.getItems();
	        while (index < items.length) {
	            TreeItem child = items[index];
	            if (child.getGrayed() || checked != child.getChecked()) {
	                checked = grayed = true;
	                break;
	            }
	            index++;
	        }
	    }
	    item.setChecked(checked);
	    item.setGrayed(grayed);
	    checkPath(item.getParentItem(), checked, grayed);
	}

	static protected void checkItems(TreeItem item, boolean checked) {
	    item.setGrayed(false);
	    item.setChecked(checked);
	    TreeItem[] items = item.getItems();
	    for (int i = 0; i < items.length; i++)
	        checkItems(items[i], checked);
	}
	
	public IType[] getSelectedTypes(){
		return getSelectedTypes(tree.getTopItem());
	}
	
	protected IType[] getSelectedTypes(TreeItem item){
		IType[] types = new IType[]{};
		if(item.getChecked() && item.getData() instanceof IType)
			types = ArrayUtils.addAll(types, (IType) item.getData());
		
		if(item.getItemCount() > 0 && !(item.getData() instanceof IType)) //Don't select inner classes
			for(TreeItem child : item.getItems())
				types = ArrayUtils.addAll(types, getSelectedTypes(child));
		return types;
	}
	
	public void setSelectedTypes(IType[] selectedTypes){
		setSelectedTypes(tree.getTopItem(), selectedTypes);
	}
	
	protected boolean setSelectedTypes(TreeItem item, IType[] selectedTypes){
		boolean childChecked = false;
		if(item.getData() instanceof IType && ArrayUtils.indexOf(selectedTypes, (IType) item.getData()) >= 0){
			checkItems(item, true);
			checkPath(item, true, false);
			childChecked = true;
		}
		if(item.getItemCount() > 0)
			for(TreeItem child : item.getItems()){
				boolean c = setSelectedTypes(child, selectedTypes);
				childChecked = childChecked || c;
			}
		if(childChecked)
			item.setExpanded(true);
		return childChecked;
	}

	public Tree getTree() {
		return tree;
	}

	public void setTree(Tree tree) {
		this.tree = tree;
	}
	
	protected Image getImage(IJavaElement javaElement){
		ImageDescriptor descriptor = null;
		if(javaElement instanceof IJavaProject)
			descriptor = JavaUI.getSharedImages().getImageDescriptor(org.eclipse.jdt.ui.ISharedImages.IMG_OBJS_PACKFRAG_ROOT);
		else if(javaElement instanceof IPackageFragmentRoot)
			descriptor = JavaUI.getSharedImages().getImageDescriptor(org.eclipse.jdt.ui.ISharedImages.IMG_OBJS_PACKFRAG_ROOT);
		else if(javaElement instanceof IPackageFragment)
			descriptor = JavaUI.getSharedImages().getImageDescriptor(org.eclipse.jdt.ui.ISharedImages.IMG_OBJS_PACKAGE);
		else if(javaElement instanceof ICompilationUnit)
			descriptor = JavaUI.getSharedImages().getImageDescriptor(org.eclipse.jdt.ui.ISharedImages.IMG_OBJS_CUNIT);
		else if(javaElement instanceof IType)
			descriptor = JavaUI.getSharedImages().getImageDescriptor(org.eclipse.jdt.ui.ISharedImages.IMG_OBJS_CLASS);
		
		if(descriptor != null){
		   //obtain the cached image corresponding to the descriptor
		   Image image = imageCache.get(descriptor);
		   if (image == null) {
		       image = descriptor.createImage();
		       imageCache.put(descriptor, image);
		   }
		   return image;
		}
		return null;
	}

}
